#include "arithmetic.hpp"



#define EOF_CHAR    (UCHAR_MAX + 1)

/* liczba bitów w której przetrzymywany jest znacznik */
#define PRECISION           (8 * sizeof(unsigned short))

/* 2 bity less than precision. keeps lower and upper bounds from crossing. */
#define MAX_PROBABILITY     (1 << (PRECISION - 2))

#define MASKA(x) (unsigned short)(1 << (PRECISION - (1 + (x))))

#define LOWER(c)        (c)
#define UPPER(c)    ((c) + 1)

unsigned short ranges[UPPER(EOF_CHAR) + 1];
unsigned short skum_prawd;   /* skumulowane prawdopodobieñstwo wszystkich przedzia³ow */

using namespace std;

unsigned short code;           /* MSBs zakodowanego pliku wejciowego */

//unsigned char niedomiar;      /* liczba bitów niedomiaru */

/* przedzia³y prawdopodobieñstwa: [ranges[LOWER(c)], ranges[UPPER(c)]) */

int bit_file::WriteBit(const int c)
{
    int Value = c;

    if (!file.is_open())
    {
        return(EOF);
    }

    counter++;
    bufor <<= 1;

    if (c != 0)
    {
        bufor |= 1;
    }

    /* zapisuje bufor jeli pe³ny */
    if (counter == 8)
    {
        file.put(bufor);


        /* reset buffer */
        counter = 0;
        bufor = 0;
    }

    return Value;
}


int bit_file::WriteBits(void *bity, const unsigned int count)
{
    unsigned char *bajty, tmp;
    int offset, remaining, Value;

    bajty = (unsigned char *)bity;


    if ((bity == NULL))
    {
        return(EOF);
    }


    offset = 0;
    remaining = count;

    /* zapisuje ca³y bajt */
    while (remaining >= 8)
    {

        //file.put(bajty[offset]);
        Value = WriteChar(bajty[offset]);
        if (Value == EOF)
        {
            return EOF;
        }
        remaining -= 8;
        offset++;
    }

    /* zapisuje pozosta³e bity*/
    tmp = bajty[offset];
    while (remaining > 0)
    {

        //file.put(tmp & 0x80);
        Value = WriteBit(tmp & 0x80);
       if (Value == EOF)
        {
            return EOF;
        }
        tmp <<= 1;
        remaining--;
    }

    return count;
}



int bit_file::WriteChar(const int c)
{
    unsigned char tmp;

    if (!file.is_open())
    {
        return(EOF);
    }

    if (counter == 0)
    {
        /* poprostu zapisuje znak do pliku */
        file.put(c);
        return c;
    }

    /* odczytuje co trzeba zapisaæ */
    tmp = ((unsigned char)c) >> counter;
    tmp = tmp | bufor << (8 - counter);

    file.put(tmp);

    if (tmp != EOF)
    {
        /* zapisuje pozosta³ê bity */
        bufor = c;
    }
    else
    {
        return EOF;
    }

    return tmp;
}
int bit_file::ReadBit()
{
    int value;

    if(counter==0)
    {
        value=file2.get();

        //bufor pusty wczytuje znak
        if (value==EOF)
        {
//            cout <<"EOF\n";
            // case EOF
            return EOF;
        }
        else{
            counter=8;
            bufor=value;
            //return bufor;
        }
    }

    counter--;
    value = bufor >> counter;
//    cout <<"wartosc bita "<<(value & 0x01) <<endl;
    return (value & 0x01);
}

int bit_file::ReadBits(void *bity, const unsigned int count)
{
    unsigned char *bajty, shifts;
    int offset, remaining, Value;

    bajty = (unsigned char *)bity;

    if (!file2.is_open() || (bity == NULL))
    {
        return(EOF);
    }

    offset = 0;
    remaining = count;

    /* wczytaj ca³y bajt */
    while (remaining >= 8)
    {
        Value = ReadChar();

        if (Value == EOF)
        {
            return EOF;
        }


        bajty[offset] = (unsigned char)Value;
        remaining -= 8;
        offset++;
    }

    /* wczytuje pozosta³e bity */
    shifts = 8 - remaining;
    while (remaining > 0)
    {
        Value = ReadBit();

        if (Value == EOF)
        {
            return EOF;
        }

        bajty[offset] <<= 1;
        bajty[offset] |= (Value & 0x01);
        remaining--;
    }

    /* przesuwa ostatnie bity */
    bajty[offset] <<= shifts;

    return count;
}

int bit_file::ReadChar()
{
    int Value;
    unsigned char tmp;

    if (!file2.is_open())
    {
//        cout <<"file2 is close must be opened to proceed\n";
        return(EOF);
    }

    Value = file2.get();


    if (counter == 0)
    {
        /* w³anie wczytalimy bajt */
        return Value;
    }

    /* w buforze pozosta³y jakie bity wczytania */
    if (Value != EOF)
    {
        /* odgaduje co odkodowa³ */
        tmp = ((unsigned char)Value) >> (counter);
        tmp |= ((bufor) << (8 - (counter)));

        /* od³ó¿ pozosta³e na bufor. counter nie powinien sie zmieniæ. */
        bufor = Value;

        Value = tmp;
    }
//    cout <<"return"<<endl;
    return Value;
}

bit_file::bit_file(char *name, bool FLG)
{
    if (FLG==1) file2.open(name,ios::in);   // otwiera plik do kodowania
    else    file.open(name,ios::out);
    counter=0;
    bufor=0;
}
bit_file::~bit_file()
{
    file.close();
    file2.close();
}

void Arythmetic::Write_ramain()
{
    if((lower & MASKA(1)) != 0)
    bitFile->WriteBit((lower & MASKA(1)) != 0);
    for (niedomiar++; niedomiar > 0; niedomiar--)
    {
        if((lower & MASKA(1)) == 0)
        bitFile->WriteBit((lower & MASKA(1)) == 0);

    }
    for(int i = bitFile->counter; i<8; i++)
        bitFile->WriteBit(0);
}

int Arythmetic::Encode(char *inFile,char *temp)
{
    int c;

    if (!Get_probab(inFile))
    {
        cerr <<" blad przy obliczaniu zakresow \n";
        return false;
    }
    ifstream input(inFile, ios::in);
    bitFile=new bit_file(temp,0);

    if (!input.is_open() or !bitFile->file.is_open()){
        cerr <<"blad otwarcia pliku w Encode \n";
        return -1;
    }
    Write_header();     ///OK
    //bitFile->file.put('e');
    /* inicjalizujemy koder na ca³y przedzia³ */
    lower = 0;  // fill with 0s
    upper = ~0; // alls 1s
    niedomiar = 0;

    input.seekg(0,ios::beg);  //ustawienie znacznika wejscia na poczatek pliku

    /* odkodowauje jesden symbol */

    while (!input.eof())
    {
        c=input.get();//zwraca int
        if (c==EOF) break;
//        cout <<"koduje znak "<<(char)c<<endl;
        //bitFile->file.put('a');
        Update_range(c);    // koduj znak
        Save_encoded();     // zapisz bity jeli mo¿liwe
    }

    Update_range(EOF_CHAR);    /* koduje  EOF */
    Save_encoded();
    Write_ramain();         /* wypisuje LSBs */
//
    input.close();
    bitFile->file.close();
    delete bitFile;

    return true;
}

void Arythmetic::Update_range(int symbol)
{
    unsigned long range;        /* musi byæ w stanie przetrzymywaæ  max upper + 1 */
    unsigned long rescaled;     /* preskaler dla nowego symbolu */
                                /* musi rzetrzymywaæ range * max upper  */

    range = (unsigned long)(upper - lower) + 1;     // wylicza obecny przedzia³

    /* skalowanie górnego przedzi³u dla nowego symbolu*/
    rescaled = (unsigned long)ranges[UPPER(symbol)] * range;
    rescaled /= (unsigned long)skum_prawd;

    /* nowy upper = stary lower + rescaled new upper - 1*/
    upper = lower + (unsigned short)rescaled - 1;

    /* skalowanie górnego przedzi³u dla nowego symbolu*/
    rescaled = (unsigned long)ranges[LOWER(symbol)] * range;
    rescaled /= (unsigned long)skum_prawd;

    /* nowy lower = stary lower + rescaled new upper */
    lower = lower + (unsigned short)rescaled;
}
bool Arythmetic::Get_probab(char *fileName)      /// poprawiono
{
    unsigned long cArray[unsigned (EOF_CHAR)];  // tablica czestosci wyst znaków
    unsigned long totalCount=0; //  ilosc wszystkich znaków w pliku
    unsigned long rescaleValue; //  preskaler
    int i;
    int c;

    ifstream input(fileName,ios::in);
    if(input.is_open()==0)
    {
        cerr <<" Nie uda³o sie otworzyc pliku w funkcji Get_probab\n";
        return false;
    }

    for (i=0; i<EOF_CHAR; i++)       // zerowanie tabicy czestosci znakow
        cArray[i]=0;


    while (!input.eof())
    {
        c=input.get();
        if (totalCount==ULONG_MAX)
        {

            return false;
        }
        cArray[c]++;
        totalCount++;
    }

       /* przeskalowuje czestosci aby byly mniejsze od < MAX_PROBABILITY */
    if (totalCount >= MAX_PROBABILITY)
    {
        rescaleValue = (totalCount / MAX_PROBABILITY) + 1;

        for (c = 0; c < EOF_CHAR; c++)
        {
            if (cArray[c] > rescaleValue)
            {
                cArray[c] /= rescaleValue;
            }
            else if (cArray[c] != 0)
            {
                cArray[c] = 1;
            }
            //cout <<cArray[c]<<endl;
        }
    }


    /* kopiowanie przeskalowanych czestosci do tablicy przedzi³ów */
    ranges[0] = 0;
    skum_prawd = 0;
    for (c = 0; c < EOF_CHAR; c++)
    {
        ranges[UPPER(c)] = cArray[c];
        skum_prawd += cArray[c];
    }

    /* zmiena czestosci na przedzial */
    Count_to_range();

    input.close();
    return true;
}
void Arythmetic::Count_to_range()
{
    int c;

    ranges[0] = 0;                              /* ustawienie lewej granicy na 0    */
    ranges[UPPER(EOF_CHAR)] = 1;        /* ustawienie 1 dla EOF ostatniego symbolu*/
    skum_prawd++;
    /* przypisuje lewy i prawy przedzial prawdopodobienstwa */
    for (c = 1; c <= UPPER(EOF_CHAR); c++)
    {
        ranges[c] += ranges[c - 1];
    }
    return;
}
void Arythmetic::Save_encoded()
{
    while (1)
    {
        if ((upper & MASKA(0)) == (lower & MASKA(0))) // sprawdzamy czy MSB upper i lower sa takie same
        {
            //  mozemy je zapisac
           bitFile->WriteBit((upper & MASKA(0))!=0);

            /* zapisujemy bity niedomiaru */
            while (niedomiar > 0)
            {
                bitFile->WriteBit((upper & MASKA(0)) == 0);
                niedomiar--;
            }
        }
        else if ((lower & MASKA(1)) && !(upper & MASKA(1)))   // sprawdzamy czy 2 bity MSB sie ró¿ni¹
        {
            niedomiar += 1;
            lower &= ~(MASKA(0) | MASKA(1));  // zeruje 2 bit MSB
            upper |= MASKA(1);
        }
        else
        {
            /* koniec */
            return ;
        }

        lower <<= 1;
        upper <<= 1;
        upper |= 1;

    }
}
void Arythmetic::Write_header()
{
    int c;
    unsigned short previous = 0;         /* zlicza symbole */

    int bit;

    for(c = 0; c <= (EOF_CHAR - 1); c++)
    {
        if (ranges[UPPER(c)] > previous)
        {

            bitFile->WriteChar((char)c);

//            cout <<(int) c<<"\t";
            previous = (ranges[UPPER(c)] - previous);   /* czestosc symbolu */

            /* zapisuje PRECISION - 2 bity, pojedynczo (LSB najpierw) */
            for (bit = 0; bit < (int)(PRECISION - 2); bit++)
            {
//                cout <<(previous&1);
                bitFile->WriteBit((previous & 1));
                previous >>= 1;
            }
//            cout <<endl;

            /* current upper range is previous for the next character */
            previous = ranges[UPPER(c)];
        }
    }

    /* zapisuje 0x00 jako znacznik koñca nag³ówka*/
    //bitFile->file.put('c');
    bitFile->WriteChar(0x00);
        previous = 0;
        bitFile->WriteBits((void *)&previous, PRECISION - 2);

}

int Arythmetic::Decode(char *dataOut,char *temp)
{
    int c;
    unsigned short  unscaled;
    ofstream fileOut(dataOut,ios::out);

    bitFile = new bit_file(temp,1);
        bitFile->file2.close();
    bitFile->file2.open(temp,ios::in);

    /* otwiera plik wyjciowy */
    if ((!fileOut.is_open()) || (!bitFile->file2.is_open()))
    {
        cerr <<"blad otwarcia pliku w funkcji Decode\n";
        return false;
    }

    bitFile->file2.seekg(0,ios::beg);

    /* pobiera dane z nag³ówka */
    if (Read_header() == false)
    {
        cerr <<"Zle wczytany naglowek w funkcji Decode \n";
        fileOut.close();
        return false;
    }

    Init_decoder();   /* wczytuje poczatek danych i ustawia przedzial */

    /* dekodowanie jednego symbolu */
    while(1)
    {
//        cout <<"czytam znak \n";
        /* okrela prawdopodobieñstwo danego symbolu */
        unscaled = Unscale_code();

        /* okresla który symbol ma dane prawdopodobieñstwo */
        if((c = Decode_symbol(unscaled)) == -1)
        {
            /* nieznany symbol */

            break;
        }

        if (c == EOF_CHAR)
        {
            /* brak symboli*/

            break;
        }

        fileOut.put(c);
//        cout <<"odkodowana wartosc ("<<c<<")\n";
        /* update'uje przedzia³y i wczytuje kolejke zakodowane bity */
        Update_range(c);
        Load_encoded();
    }
    bitFile->file2.close();
    bitFile->file.close();
    delete bitFile;

    fileOut.close();


    return true;
}



int Arythmetic::Read_header()
{

    int c;
    unsigned short count;

    int i, nextBit;

//    bitFile->counter=0;

    skum_prawd = 0;

    for (c = 0; c <= UPPER(EOF_CHAR); c++)
    {
        ranges[UPPER(c)] = 0;
    }

    /* czyta dane w formie [character, probability]  */
    for (;;)
    {
        c = bitFile->ReadChar();

//        cout <<"wczytany znak to "<<c<<endl;

        count = 0;

        /* czyta PRECISION - 2 bity pojedyñczo (LSB najpierw) */
        for (i = 0; i < (int)(PRECISION - 2); i++)
        {


            nextBit = bitFile->ReadBit();
            if (nextBit== EOF)
            {
                /* premature EOF */
                cerr <<"error\n";
                return false;
            }
            else if (nextBit == 1)
            {

                /* or in 1 bit */
                count |= (1 << i);
            }
        }
        //cout <<count<<endl;
        if (count == 0)
        {

            break;
        }

        ranges[UPPER(c)] = count;
        skum_prawd += count;
    }

    /* konweruje czestosci na przedzialy */
    Count_to_range();
    return true;
}

void Arythmetic::Init_decoder()
{
    int i;
    code = 0;

    /* czyta PRECISION msbs pliku zakodowanego */
    for (i = 0; i < (int)(PRECISION); i++)
    {
        code <<= 1;

        /* traktuje EOF jak 0 */
        if(bitFile->ReadBit() == 1)
        {
            code |= 1;
        }
    }
    /* ustawia przedzia³  [0%, 100%) */
    lower = 0;
    upper = ~0;         /* all ones */
}

unsigned short Arythmetic::Unscale_code(void)
{
    unsigned long range;
    unsigned long unscaled;

    range = (unsigned long)(upper - lower) + 1;

    /* odwraca operacje skalowania */
    unscaled = (unsigned long)(code - lower) + 1;
    unscaled = unscaled * (unsigned long)skum_prawd - 1;
    unscaled /= range;

    return ((unsigned short)unscaled);
}

int Arythmetic::Decode_symbol(unsigned short probability)
{
    int c;

    for (c = 0; c <= EOF_CHAR; c++)
    {
        if ((probability >= ranges[LOWER(c)]) &&
            (probability < ranges[UPPER(c)]))
        {
            return c;
        }
    }

    /* error: nie ma takiedo przdzialu */
    cerr <<"Unknown Symbol: "<< probability<<"(max: "<<ranges[UPPER(EOF_CHAR)]<<endl;
    return -1;
}

void Arythmetic::Load_encoded()
{
    int nextBit;        /* nastepny z zakodowanego pliku */

    while (1)
    {
        if (( upper & MASKA(0)) == (lower & MASKA(0)))
        {
                        /* MSBs sa takie same */
        }
        else if ((lower & MASKA(1)) && !(upper & MASKA(1)))
        {

                        lower   &= ~(MASKA(0) | MASKA(1));
            upper  |= MASKA(1);
            code ^= MASKA(1);

                }
        else
        {

                        return;
        }

                lower <<= 1;
        upper <<= 1;
        upper |= 1;
        code <<= 1;

        if ((nextBit = bitFile->ReadBit()) == EOF)
        {

        }
                else
                {
                code |= nextBit;
                }
    }

    return;
}

